import click
import json
import mimetypes
from pathlib import Path
import subprocess
from specimen_utils import sha256hex
import structured_fingerprint


def mimetype(url):
    maybe_type = mimetypes.guess_type(url)[0]
    if maybe_type is None:
        raise RuntimeError(f'could not guess mime-type of "{url}"')
    return maybe_type


def asset_transform_fingerprints(asset_metadata_file):
    # This is horrible but we want to match JavaScript's
    # floating-point representation exactly.
    this_dir = Path(__file__).resolve().parent
    js_path = this_dir / "transform-fingerprints.js"
    result = subprocess.run(
        ["node", js_path],
        stdin=asset_metadata_file,
        capture_output=True,
    )
    return json.loads(result.stdout)


def program_fingerprint(program):
    kind = program["kind"]
    if kind == "flat":
        hash = sha256hex(program["text"])
        return f"program=flat/{hash}"
    elif kind == "per-method":
        inner = structured_fingerprint.of_program(program["program"])
        return f"program=per-method/{inner}"
    else:
        raise RuntimeError(f'unknown program-kind "{kind}"')


def project_fingerprint(root_dir):
    with (root_dir / "code" / "code.json").open("rb") as f_in:
        code_obj = json.load(f_in)
        program_kind = code_obj["kind"]
        program_fingerprint_line = program_fingerprint(code_obj)

    with (root_dir / "assets" / "metadata.json").open("rb") as f_in:
        asset_fingerprint_records = asset_transform_fingerprints(f_in)
        transform_fingerprint_from_asset = {
            record["name"]: record["fingerprint"]
            for record in asset_fingerprint_records
        }

    def asset_fingerprint(asset_filename):
        asset_file = root_dir / "assets" / "files" / asset_filename
        name_hash = sha256hex(asset_filename)
        mimetype_hash = sha256hex(mimetype(asset_filename))
        content_hash = sha256hex(asset_file.read_bytes())
        transform_hash = sha256hex(transform_fingerprint_from_asset[asset_filename])
        fingerprint = f"{name_hash}/{mimetype_hash}/{content_hash}/{transform_hash}"
        return fingerprint

    # TODO: Avoid reading this file twice:
    with (root_dir / "assets" / "metadata.json").open("rb") as f_in:
        asset_records = json.load(f_in)
        raw_asset_fingerprints = [
            asset_fingerprint(asset_record["name"])
            for asset_record in asset_records
        ]

    asset_fingerprints = (
        sorted(raw_asset_fingerprints)
        if program_kind == "flat"
        else raw_asset_fingerprints
    )
    assets_fingerprint = f"assets={','.join(asset_fingerprints)}"

    return f"{program_fingerprint_line}\n{assets_fingerprint}\n"


def project_content_hash(root_dir):
    return sha256hex(project_fingerprint(root_dir))


@click.command()
@click.argument(
    "root_path", type=click.Path(exists=True, file_okay=False, dir_okay=True)
)
@click.option(
    "--with-filename/--no-filename",
    default=False,
    help="Print the ROOT_PATH before the content-hash."
)
def main(root_path, with_filename):
    """Print the content-hash of the project at the given ROOT_PATH."""
    if with_filename:
        print(root_path, end=" ")
    print(project_content_hash(Path(root_path)))


if __name__ == "__main__":
    main()
